#include "Map.h"
#include "Constants.h"
#include <algorithm>
#include <particles\ParticleSystem.h>

const int XM[4] = {1,-1,0,0};
const int YM[4] = {0,0,1,-1};
const int LD[4] = {1,0,3,2};

Map::Map(Gutter* gutter) : ds::DynamicSpriteNode("Cubes","TexturesMaterial") , m_Gutter(gutter) {
	m_Grid = new ds::GridArray<Entry>(28,14);
	Entry e;
	e.color = -1;
	e.sprite = 0;
	e.marked = false;
	e.timer = 0.0f;
	m_Grid->clear(e);	
	m_CleanTimer = 0.0f;
	m_Emitter = new BlockEmitter();
}


Map::~Map(void) {	
	delete m_Grid;
}

// --------------------------------------------
//
// --------------------------------------------
bool Map::addBlock(const ds::Vec2& pos,int firstColor,int secondColor,int thirdColor,int fourthColor) {
	int x = ( pos.x - START_X ) / 35;
	int y = ( pos.y - START_Y ) / 35;	
	if ( m_Grid->isValid(x,y) && isBlockAvailable(x,y) && m_Gutter->isBlockAvailable(x,y) ) {
		addEntry(x,y,firstColor);
		addEntry(x+1,y,secondColor);
		addEntry(x+1,y+1,thirdColor);
		addEntry(x,y+1,fourthColor);	
		//gEngine->getAudioManager().play("Move",100,false);
		for ( int i = 0; i < 4; ++i ) {
			int cnt = checkSurrounding(x + BLOCK_X[i], y + BLOCK_Y[i]);			
		}				
		return true;		
	}
	else {
		gEngine->getAudioManager().play("NoMove",30,false);
	}
	return false;
}

// --------------------------------------------
//
// --------------------------------------------
bool Map::isBlockAvailable(int gx,int gy) {
	for ( int i = 0; i < 4; ++i ) {
		if ( !isFree( gx + BLOCK_X[i], gy + BLOCK_Y[i]) ) {
			return false;
		}
	}	
	return true;
}

// --------------------------------------------
//
// --------------------------------------------
bool Map::isFree(int gx,int gy) {
	Entry& e = m_Grid->get(gx,gy);
	if ( e.color == -1 ) {
		return true;
	}
	return false;
}

// --------------------------------------------
//
// --------------------------------------------
void Map::markBlocks() {
	for ( int x = 0; x < MAX_X; ++x ) {
		for ( int y = 0; y < MAX_Y; ++y ) {
			Entry& e = m_Grid->get(x,y);
			if ( isCoherent(x,y)) {
				markSingleBlock(x,y);
			}
		}
	}
}

// --------------------------------------------
//
// --------------------------------------------
void Map::markSingleBlock(int gx,int gy) {
	for ( int i = 0; i < 4; ++i ) {
		Entry& e = m_Grid->get( gx + BLOCK_X[i], gy + BLOCK_Y[i]);
		if ( !e.marked ) {			
			int offset = e.color * 38;
			e.sprite->offset = ds::Rect(110,offset,36,36);
			e.sprite->position.sub(2.0f,2.0f);
			e.marked = true;
		}
	}	
	Entry& ef = m_Grid->get(gx,gy);
	ds::Vec2 p = ef.sprite->position;	
}

// --------------------------------------------
//
// --------------------------------------------
bool Map::markSingleEntry(int gx,int gy) {
	Entry& e = m_Grid->get( gx, gy);
	if ( !e.marked ) {			
		int offset = e.color * 38;
		e.sprite->offset = ds::Rect(110,offset,36,36);
		e.sprite->position.sub(2.0f,2.0f);
		e.marked = true;		
		return true;
	}		
	return false;
}

// --------------------------------------------
//
// --------------------------------------------
bool Map::isCoherent(int gx,int gy) {
	Entry& be = m_Grid->get(gx,gy);
	int color = be.color;
	if ( color != -1 ) {
		int cnt = 0;
		for ( int i = 1; i < 4; ++i ) {
			Entry& e = m_Grid->get( gx + BLOCK_X[i], gy + BLOCK_Y[i]);
			if ( e.color != -1 && e.color == color ) {
				++cnt;
			}
		}
		if ( cnt == 3 ) {
			return true;
		}
	}
	return false;
}

// --------------------------------------------
//
// --------------------------------------------
void Map::addEntry(int gx,int gy,int color) {
	ds::Vec2 p = ds::Vec2(START_X + gx * SQUARE_SIZE,START_Y + gy * SQUARE_SIZE);
	Entry& e = m_Grid->get(gx,gy);
	e.sprite = new ds::Sprite(p,ds::Rect(72,color*34,32,32));	
	e.color = color;
}

// --------------------------------------------
//
// --------------------------------------------
void Map::reset() {
	for ( int x = 0; x < MAX_X; ++x ) {
		for ( int y = 0; y < MAX_Y; ++y ) {
			Entry& e = m_Grid->get(x,y);
			if ( e.sprite != 0 ) {
				delete e.sprite;
				e.sprite = 0;
				e.marked = false;
				e.color = -1;
			}			
		}
	}
}

// --------------------------------------------
//
// --------------------------------------------
void Map::cleanMarkedBlocks() {
	for ( int x = 0; x < MAX_X; ++x ) {
		for ( int y = 0; y < MAX_Y; ++y ) {
			Entry& e = m_Grid->get(x,y);
			if ( e.sprite != 0 && e.marked ) {
				delete e.sprite;
				e.sprite = 0;
				e.marked = false;
				e.color = -1;
			}			
		}
	}
}

// --------------------------------------------
//
// --------------------------------------------
int Map::cleanRow(int row) {
	int cnt = 0;
	if ( m_Grid->isValidRow(row)) {		
		for ( int y = 0; y < MAX_Y; ++y ) {
			Entry& e = m_Grid->get(row,y);
			if ( e.sprite != 0 && e.marked ) {
				m_Emitter->setColorIndex(e.color);		
				ds::ParticleManager* pm = static_cast<ds::ParticleManager*>(gEngine->getWorld().getNode("Particles"));
				ds::ParticleSystem* ps = pm->prepare("BlockExplosion");
				ps->setEmitter(m_Emitter,true);
				pm->start(ds::Vec3(row,y,0),ps);
				if ( !gEngine->getAudioManager().isPlaying("Bell") ) {
					gEngine->getAudioManager().play("Bell",100,false);
				}
				//m_Particles->startBlock(row,y,e.color);
				delete e.sprite;
				e.sprite = 0;
				e.marked = false;
				e.color = -1;
				m_Gutter->markCell(row,y);	
				++cnt;
			}			
		}
	}
	return cnt;
}

// --------------------------------------------
//
// --------------------------------------------
void Map::update( float elapsed ) {	
	for ( int x = 0; x < MAX_X; ++x ) {
		for ( int y = 0; y < MAX_Y; ++y ) {
			Entry& e = m_Grid->get(x,y);
			if ( e.marked ) {
				e.timer += elapsed;
				e.sprite->color.a = 0.9f + sin(e.timer * 5.0f) * 0.1f;
			}
			if ( e.sprite != 0 ) {
				addSprite(e.sprite);
			}
		}
	}	
}

int Map::checkSurrounding(int xp,int yp) {
	std::vector<ds::Vec2> colors;
	std::vector<int> used;
	int cnt = 0;
	//LOG(logINFO) << "check surrounding at " << xp << " " << yp;
	check(xp,yp,-1,&colors,true);	
	if ( colors.size() > 2 ) {
		//LOG(logINFO) << "found result at " << xp << " " << yp;
		//LOG(logINFO) << "colors " << colors.size();
		int idx = xp + yp * MAX_X;
		used.push_back(idx);
		if ( markSingleEntry(xp,yp) ) {
			++cnt;
		}
		for ( size_t i = 0; i < colors.size(); ++i ) {
			ds::Vec2 p = colors[i];
			idx = p.x + p.y * MAX_X;
			std::vector<int>::iterator it = find(used.begin(), used.end(), idx);
			if ( it == used.end() ) {
				++cnt;
				used.push_back(idx);
			}
			markSingleEntry(p.x,p.y);
		}			
		//LOG(logINFO) << "marked blocks " << cnt;
		//return colors.size()+1;
		return cnt;
	}
	return 0;	
}

// -------------------------------------------------------------
//
// -------------------------------------------------------------
void Map::check(int xp,int yp,int lastDir,std::vector<ds::Vec2> *list,bool rec) {
	if ( m_Grid->isValid(xp,yp) ) {
		Entry& e = m_Grid->get(xp,yp);
		int color = e.color;
		//LOG(logINFO) << "color " << color << " at " << xp << " " << yp;
		for ( int i = 0 ; i < 4; ++i ) {
			if ( i != lastDir ) {
				int sx = xp + XM[i];
				int sy = yp + YM[i];
				if ( m_Grid->isValid(sx,sy)) {
					Entry& ne = m_Grid->get(sx,sy);
					int nc = ne.color;
					//LOG(logINFO) << "next color " << nc << " at " << sx << " " << sy;
					while ( color == nc ) {												
						bool recheck = true;
						for ( int r = 0; r < list->size(); ++r ) {
							ds::Vec2 v = list->at(r);
							if ( v.x == sx && v.y == sy ) {
								recheck = false;
							}
						}
						//LOG(logINFO) << "found match at " << nc << " at " << sx << " " << sy;
						list->push_back(ds::Vec2(sx,sy));
						if ( recheck && rec ) {
							check(sx,sy,LD[i],list,rec);
						}												
						sx += XM[i];
						sy += YM[i];
						if ( m_Grid->isValid(sx,sy)) {
							Entry& npe = m_Grid->get(sx,sy);						
							nc = npe.color;
						}
						else {
							nc = -1;
						}						
					}
				}
			}
		}
	}
}
